package com.ding.mylib;

/**
 * 自定义进制
 */
public class Sn26Util {
    /**
     * 0-9 a-p 26 位, 可以理解为 26 进制
     */
    private static Sn26Util instance;

    public static Sn26Util getInstance() {
        if (instance == null) {
            instance = new Sn26Util();
        }
        return instance;
    }

    private char[] MySerials;

    private Sn26Util() {
        MySerials = new char[]{
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'
        };
    }

    /**
     * 获取当前 26 进制 数的索引
     * 即 一个字符时: 26 进制的 十进制表示
     *
     * @param c
     * @return
     */
    private int GetMySnIndex(String c) {
        int index = 0;
        for (int i = 0; i < MySerials.length; i++) {
            String s = String.valueOf(MySerials[i]);
            if (c.equalsIgnoreCase(s)) {
                index = i;
                break;
            }
        }
        return index;
    }


    /**
     * 目的: 获取 26 进制的下一位 Sn
     * 步骤:
     * 1. 获取26进制数,转成10进制,
     * 2.10进制数+1
     * 3.再转成26进制
     * 因为26不可直接计算
     *
     * @param start
     * @return
     */
    public String next(String start) {
        int i = Sn2Int(start);
        return Int2Sn(i + 1);
    }

    /**
     * Sn to Int
     * 26 进制数转成 10 进制
     *
     * @param start
     * @return
     */
    public int Sn2Int(String start) {
        int result = 0;
        //将字符串拆成 char 数组
        char[] chars = start.toCharArray();
        //循环数组,对每一个字符运算转成26进制, 然后相加得到10进制数
        for (int i = 0; i < chars.length; i++) {
            /**
             * 1.个位直接取26的索引,即26进制的数字
             *  十位取26进制的数字后要 乘以26 (类似10位的数字"1"表示10,"2"表示 20,即 数字*10的一次方)
             *  百位取26进制的数字后要乘以26 的二次方(类似百位的数字"1"表示100,"2"表示200,即数字*10的二次方)
             *  所以:  26进制的数字为 n*26* 的(n的位数-1 次方)
             */
            /**
             *   获取位数(十位,百位,千位): 正向循环 第一位实际是数字的最高位
             *   减1 : 取 位数-1 次方
             */

            int pow = chars.length - i - 1;

            /**
             * 索引即 26进制单字符转10进制后的数值
             */
            int index = GetMySnIndex(String.valueOf(chars[i]));
            /**
             *数字 n * 26的(位数减1次方)
             * 相加 得到所有位数的10进制数字
             */
            result += index * Math.pow(26, Double.valueOf(pow));
        }
        return result;
    }

    /**
     * 通过取模获取 26 进制的余数 index
     * 反向加到加入字符串中
     *
     * @param n
     * @return
     */
    public String Int2Sn(int n) {
        String s = "";

        if (n == 0) {
            s = "0";
        }

        //商==0 代表循环完毕
        // 小余26的数字 除以26 等于0
        while (n != 0) {
            //对26取余
            int i = n % 26;
            //获取26进制数值
            char c = MySerials[i];
            //反向加入字符串中
            s = c + s;
            //求商
            n = n / 26;
        }
        //返回结果
        return s;
    }

}
